using System;
using System.Collections.Generic;
using DirectMidi;
using System.Windows.Forms;

namespace Oxage.Midi
{
    public class DirectMidiConnector
    {
        #region Members
        //private CDMReceiver Receiver = new CDMReceiver();
        private CDirectMusic CDMusic = new CDirectMusic();
        private CInputPort CInPort = new CInputPort();
        private COutputPort COutPort = new COutputPort();
        private CDLSLoader CLoader = new CDLSLoader();
        private CCollection CCollection;
        private CInstrument CInstrument;
        private CSampleInstrument CSample = new CSampleInstrument();
        //private DirectMidiForm panel;
        private bool isOpened = false;
        #endregion

        #region Constructor
        public DirectMidiConnector()
        {
            //panel = new DirectMidiForm(this);
        }
        #endregion

        #region Methods
        public void ProgramChange(object instrument)
        {
            if (this.IsOpened)
            {
                try
                {
                    // Unloads the instruments from the port
                    COutPort.UnloadInstrument(CInstrument);

                    // Gets the instrument with index 214 from the CollectionB
                    CCollection.GetInstrument(out CInstrument, instrument as INSTRUMENTINFO);

                    // Assigns it to the MIDI program 0
                    CInstrument.SetPatch(0);
                    CInstrument.SetNoteRange(0, 127);

                    // Downloads the instruments to the output ports
                    COutPort.DownloadInstrument(CInstrument);
                }
                catch
                {
                }
            }
        }

        public void LoadDLS(string filename)
        {
            if (this.IsOpened)
            {
                try
                {
                    // Unloads the collections from the loader
                    CLoader.UnloadCollection(CCollection);

                    // Loads the default GM collection of the software synthesizer
                    CLoader.LoadDLS(filename, out CCollection);

                    //TODO: Select first instrument

                    //panel.LoadInstruments();
                }
                catch
                {
                }
            }
        }

        private List<DeviceInfo> deviceList = new List<DeviceInfo>();
        public IEnumerable<DeviceInfo> GetDevices()
        {
            try
            {
                if (deviceList == null || deviceList.Count == 0)
                {
                    deviceList = new List<DeviceInfo>();

                    //CDMusic.Initialize();
                    //COutPort.Initialize(CDMusic);
                    //CInPort.Initialize(CDMusic);

                    INFOPORT PortInfo;
                    UInt32 dwPortCount = 0;

                    // Software Synthesizer selection
                    do
                    {
                        if (COutPort.GetPortInfo(++dwPortCount, out PortInfo) == 1)
                        {
                            break;
                        }
                        deviceList.Add(new DeviceInfo() { PortInfo = PortInfo, Name = PortInfo.szPortDescription });
                    }
                    while (PortInfo.dwFlags != DMUS_PC.XGINHARDWARE);
                }
            }
            catch
            {
            }

            return deviceList;
        }

        public void ProgramChange(int instrument)
        {
            if (this.IsOpened)
            {
                // Unloads the instruments from the port
                COutPort.UnloadInstrument(CInstrument);

                // Gets the instrument with index 214 from the CollectionB
                CCollection.GetInstrument(out CInstrument, instrument);

                // Assigns it to the MIDI program 0
                CInstrument.SetPatch(0);
                CInstrument.SetNoteRange(0, 127);
                
                // Downloads the instruments to the output ports
                COutPort.DownloadInstrument(CInstrument);
            }
        }

        //private List<MidiProgram> programList = new List<MidiProgram>();
        //public IEnumerable<MidiProgram> GetProgramList()
        //{
        //    return this.programList;
        //}

        public override string ToString()
        {
            return "DirectMidi";
        }
        #endregion

        #region IConnector Members
        public bool IsOpened
        {
            get
            {
                return this.isOpened;
            }
            set
            {
                this.isOpened = value;
                //this.panel.IsLocked = value;
            }
        }

        //public DataDirection Direction
        //{
        //    get { return DataDirection.Output; }
        //}

        /*public void Open()
        {
            Open(null);
        }*/

        public void Open(string filename)
        {
            if (!this.IsOpened)
            {
                try
                {
                    //If PortInfo is set then user has requested a device change
                    //bool requestDeviceChange = (PortInfo != null);

                    CDMusic.Initialize();
                    
                    COutPort.Initialize(CDMusic);
                    //CInPort.Initialize(CDMusic);

                    //INFOPORT PortInfo;
                    UInt32 dwPortCount = 0;

                    // Software Synthesizer selection
                    /*if (PortInfo == null)
                    {
                        do
                        {
                            COutPort.GetPortInfo(++dwPortCount, out PortInfo);
                        }
                        while (0 == (PortInfo.dwFlags & DMUS_PC.SOFTWARESYNTH));
                    }*/

                    // Output port activation given the port information 
                    COutPort.SetPortParams(0, 0, 1, SET.REVERB | SET.CHORUS, 44100);
                    COutPort.ActivatePort(null);

                    // Initialize the Loader object  
                    CLoader.Initialize();

                    // Loads the default GM collection of the software synthesizer
                    CLoader.LoadDLS(null, out CCollection);


                    #region Load instruments
                    // Structure of the instrument information
                    INSTRUMENTINFO InstInfo;
                    UInt32 dwInstIndex = 0;

                    //this.programList = new List<MidiProgram>();

                    // Enumerates instruments in CollectionB
                    while ((HR_DMUS)CCollection.EnumInstrument(dwInstIndex++, out InstInfo) == HR_DMUS.HR_S_OK)
                    {
                       // this.programList.Add(new MidiProgram() { Number = (int)InstInfo.dwPatchInCollection, Name = InstInfo.szInstName, Instrument = InstInfo });
                        //Console.WriteLine("Instrument name: " + InstInfo.szInstName);
                        //Console.WriteLine("Patch in collection: " + InstInfo.dwPatchInCollection);
                        //Console.WriteLine("----------------------------------------");
                    }

                    // Gets the instrument with index 214 from the CollectionB
                    CCollection.GetInstrument(out CInstrument, 1);

                    // Assigns it to the MIDI program 0
                    CInstrument.SetPatch(0);
                    CInstrument.SetNoteRange(0, 127);

                    try
                    {
                        // Downloads the instruments to the output ports
                        COutPort.DownloadInstrument(CInstrument);

                        COutPort.SendMidiMsg(COutputPort.EncodeMidiMsg(DirectMidi.MidiMessage.PATCH_CHANGE, 0, 0, 0), 0);
                    }
                    catch
                    {
                        //Some devices (like virtual midi through cable) do not support instruments
                    }
                    #endregion

                    
                    //Update GUI
                    //panel.LoadInstruments();

                    //if (!requestDeviceChange)
                    //{
                        //Do this only for the first time
                        //panel.LoadDevices();
                    //}

                    this.IsOpened = true;

                    //Default collection
                    LoadDLS(null);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("DirectMidi: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    this.IsOpened = false;
                }
            }
            //panel.RefreshControls();
        }

        public void Close()
        {
            try
            {
                // Unloads the collections from the loader
                //CLoader.UnloadCollection(CCollectionA);
                CLoader.UnloadCollection(CCollection);

                // Unloads the instruments from the port
                COutPort.UnloadInstrument(CInstrument);
                //COutPort.UnloadInstrument(CInstrument2);

                //// Unloads the sample instruments
                //COutPort.UnloadInstrument(CSample1);
                //COutPort.UnloadInstrument(CSample2);

                //// Frees allocated memory
                //COutPort.DeallocateMemory(CSample1);
                //COutPort.DeallocateMemory(CSample2);

                COutPort.ReleasePort();
                CInPort.ReleasePort();
            }
            catch
            {
            }

            this.IsOpened = false;
            //panel.RefreshControls();
        }

        /*public void Send(IMessage message)
       {
            if (this.IsOpened && message is MidiMessage)
            {
                uint e = 0;
                var midi = message as MidiMessage;
                switch (midi.Event)
                {
                    case MidiEvents.NoteOn:
                        e = COutputPort.EncodeMidiMsg(DirectMidi.MidiMessage.NOTE_ON, midi.Channel, midi.Note, midi.Velocity);
                        break;

                    case MidiEvents.NoteOff:
                        e = COutputPort.EncodeMidiMsg(DirectMidi.MidiMessage.NOTE_OFF, midi.Channel, midi.Note, midi.Velocity);
                        break;
                }
                if (e > 0)
                {
                    COutPort.SendMidiMsg(e, 0);
                }
            }
        }*/

        //public event EventHandler<EventArgs<IMessage>> Received;
        /*protected virtual void OnReceived(IMessage message)
        {
            if (Received != null)
            {
                Received(this, new EventArgs<IMessage>(message));
            }
        }

        public IConfigPanel GetConfigPanel()
        {
            return panel;
        }*/
        #endregion
    }

    public class DeviceInfo
    {
        /// <summary>
        /// Gets or sets a human friendly name of the device.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the handle and device info.
        /// </summary>
        public INFOPORT PortInfo
        {
            get;
            set;
        }

        /// <summary>
        /// Displays human friendly name in ComboBox.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.Name;
        }
    }
}
